package com.polarrose.wsf.client.impl;

import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.nio.channels.Channels;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xlightweb.BlockingBodyDataSource;
import org.xlightweb.IHttpResponse;
import org.xlightweb.PostRequest;
import org.xlightweb.client.HttpClient;

import com.polarrose.wsf.client.HttpProtocolException;
import com.polarrose.wsf.client.Invocation;
import com.polarrose.wsf.client.RequestInvoker;
import com.polarrose.wsf.client.ResultTransformationException;
import com.polarrose.wsf.client.WsfClientException;

public class XLightwebRequestInvoker implements RequestInvoker {

	private Logger logger = LoggerFactory.getLogger(XLightwebRequestInvoker.class);

	public static final String PARAM_VERSION = "Version";
	public static final String PARAM_ACTION = "Action";

	private HttpClient httpClient;

	public XLightwebRequestInvoker() {
		httpClient = new HttpClient();
		httpClient.setBodyDataReceiveTimeoutMillis(10000);
		httpClient.setFollowsRedirect(true);
		httpClient.setMaxActive(250);
		httpClient.setMaxIdle(50);
		httpClient.setMaxRedirects(3);
		httpClient.setPooled(true);
		httpClient.setResponseTimeoutMillis(10000);
	}

	public void invokeRequest(Invocation<?> invocation) throws WsfClientException {
		String endpoint = invocation.getEndpoint();
		String action = invocation.getAction();
		String version = invocation.getVersion();

		Map<String, String> parameters = invocation.getParameters();

		try {
			PostRequest method = new PostRequest(endpoint);
			method.setContentType("application/x-www-form-urlencoded; charset=UTF-8");
			method.setConnection("close");
	
			method.setParameter(PARAM_ACTION, action);
			method.setParameter(PARAM_VERSION, version);
	
			if (parameters != null) {
				for (Entry<String, String> parameter : parameters.entrySet()) {
					method.setParameter(parameter.getKey(), parameter.getValue());
				}
			}

			logger.debug("Requesting URL {}?{}", endpoint, formatParameters(method));

			IHttpResponse httpResponse = httpClient.call(method);

			if (httpResponse.getStatus() == 200) {
				String response = httpResponse.getBlockingBody().readString();
				invocation.getResultTransformer().transform(response);
			} else {
				throw new HttpProtocolException(httpResponse.getStatus(), httpResponse.getBlockingBody().readString());
			}
		} catch (SocketTimeoutException e) {
			throw new WsfClientException("Socket timeout occured.", e);
		} catch (IOException e) {
			throw new WsfClientException("An I/O error occured.", e);
		} catch (ResultTransformationException e) {
			throw new WsfClientException("Could not transform JSON response.", e);
		}
	}

	private Object formatParameters(PostRequest method) {
		StringBuilder buff = new StringBuilder();
		Set<String> parameterNames = method.getParameterNameSet();
		for (String paramName : parameterNames) {
			buff.append(paramName).append("=").append(Arrays.toString(method.getParameterValues(paramName))).append("&");
		}
		return buff.substring(0, buff.length() - 1);
	}

}
